module data_ram #(
  parameter DEPTH = 4096
) (
  input             clk,
  input             rst_n,
  input             mem_req,
  input             mem_rwn,
  input      [1:0]  mem_size,
  input      [31:0] mem_addr,
  input      [31:0] mem_wdata,
  output reg        mem_grant,
  output reg [31:0] mem_rdata
);

  localparam WordBytes = 4;
  localparam WordWidth = WordBytes*8;
  localparam AddrAlign = $clog2(WordBytes);
  localparam AddrWidth = $clog2(DEPTH);

  reg  [WordWidth-1:0] ram [0:DEPTH-1];

  wire [AddrAlign-1:0] byte_bias;
  wire [AddrWidth-1:0] word_addr;

  wire [7:0]  rdata_byte;
  wire [15:0] rdata_half;
  wire [31:0] rdata_word;
  reg  [31:0] rdata;

  wire new_req;

  assign byte_bias = mem_addr[AddrAlign-1:0];
  assign word_addr = mem_addr[AddrAlign+AddrWidth-1:AddrAlign];

  assign new_req = mem_req & ~mem_grant;

  assign rdata_byte = ram[word_addr][byte_bias*8-1+:8];
  assign rdata_half = ram[word_addr][byte_bias*8-1+:16];
  assign rdata_word = ram[word_addr];

  always @(*) begin
    rdata = rdata_word;
    case (mem_size)
      'b00: rdata = {24'b0, rdata_byte};
      'b01: rdata = {16'b0, rdata_half};
      'b10: rdata = rdata_word;
    endcase
  end

  always @(posedge clk, negedge rst_n) begin
    if (~rst_n) begin
      mem_grant <= 1'b0;
    end else begin
      mem_grant <= 1'b0;
      if (new_req) begin
        mem_grant <= 1'b1;
      end
    end
  end

  always @(posedge clk) begin
    if (new_req & ~mem_rwn) begin
      case (mem_size)
        'b00: ram[word_addr][byte_bias*8-1+:8]  <= mem_wdata[7:0];
        'b01: ram[word_addr][byte_bias*8-1+:16] <= mem_wdata[15:0];
        'b10: ram[word_addr]                    <= mem_wdata;
      endcase
    end
  end

  always @(posedge clk, negedge rst_n) begin
    if (~rst_n) begin
      mem_rdata <= 32'b0;
    end else if(new_req) begin
      mem_rdata <= rdata;
    end
  end

endmodule

